﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using Mesuro.Common;
using Mesuro.Graphing.Utilities;
using System.Collections.ObjectModel;

using System.Windows.Markup;
using System.Collections.Specialized;

namespace Mesuro.Graphing {
	[ContentProperty( "Plots" )]
	public abstract class Axes : Panel {
		public static readonly RoutedEvent AxesLoadedEvent = EventManager.RegisterRoutedEvent( "AxesLoaded", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( Axes ) );
		#region DP: Dimensions
		public static readonly DependencyProperty DimensionsProperty = DependencyProperty.RegisterAttached( "Dimensions", typeof( int ), typeof( Axes ), new FrameworkPropertyMetadata( 0, FrameworkPropertyMetadataOptions.None, DimensionsChangedStatic ) );
		public int Dimensions {
			get { return (int)GetValue( DimensionsProperty ); }
			set { SetValue( DimensionsProperty, value ); }
		}
		public static int GetDimensions( DependencyObject element ) { return (int)element.GetValue( DimensionsProperty ); }
		public static void SetDimensions( DependencyObject element, int value ) { element.SetValue( DimensionsProperty, value ); }
		private static void DimensionsChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: PlotArea
		public static readonly DependencyProperty PlotAreaProperty = DependencyProperty.RegisterAttached( "PlotArea", typeof( Geometry ), typeof( Axes ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, PlotAreaChangedStatic ) );
		public Geometry PlotArea {
			get { return (Geometry)GetValue( PlotAreaProperty ); }
			set { SetValue( PlotAreaProperty, value ); }
		}
		public static Geometry GetPlotArea( DependencyObject element ) { return (Geometry)element.GetValue( PlotAreaProperty ); }
		public static void SetPlotArea( DependencyObject element, Geometry value ) { element.SetValue( PlotAreaProperty, value ); }
		private static void PlotAreaChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: TextPadding
		public static readonly DependencyProperty TextPaddingProperty = DependencyProperty.RegisterAttached( "TextPadding", typeof( double ), typeof( Axes ), new FrameworkPropertyMetadata( 2d, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, TextPaddingChangedStatic ) );
		public double TextPadding {
			get { return (double)GetValue( TextPaddingProperty ); }
			set { SetValue( TextPaddingProperty, value ); }
		}
		public static double GetTextPadding( DependencyObject element ) { return (double)element.GetValue( TextPaddingProperty ); }
		public static void SetTextPadding( DependencyObject element, double value ) { element.SetValue( TextPaddingProperty, value ); }
		private static void TextPaddingChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: PlotBackground
		public static readonly DependencyProperty PlotBackgroundProperty = DependencyProperty.RegisterAttached( "PlotBackground", typeof( Brush ), typeof( Axes ), new FrameworkPropertyMetadata( Brushes.WhiteSmoke, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, PlotBackgroundChangedStatic ) );
		public Brush PlotBackground {
			get { return (Brush)GetValue( PlotBackgroundProperty ); }
			set { SetValue( PlotBackgroundProperty, value ); }
		}
		public static Brush GetPlotBackground( DependencyObject element ) { return (Brush)element.GetValue( PlotBackgroundProperty ); }
		public static void SetPlotBackground( DependencyObject element, Brush value ) { element.SetValue( PlotBackgroundProperty, value ); }
		private static void PlotBackgroundChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: ScalePen
		public static readonly DependencyProperty ScalePenProperty = DependencyProperty.RegisterAttached( "ScalePen", typeof( Pen ), typeof( Axes ), new FrameworkPropertyMetadata( new Pen( Brushes.Black, 2 ), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, ScalePenChangedStatic ) );
		public Pen ScalePen {
			get { return (Pen)GetValue( ScalePenProperty ); }
			set { SetValue( ScalePenProperty, value ); }
		}
		public static Pen GetScalePen( DependencyObject element ) { return (Pen)element.GetValue( ScalePenProperty ); }
		public static void SetScalePen( DependencyObject element, Pen value ) { element.SetValue( ScalePenProperty, value ); }
		private static void ScalePenChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: MinimumTickSpacing
		public static readonly DependencyProperty MinimumTickSpacingProperty = DependencyProperty.RegisterAttached( "MinimumTickSpacing", typeof( double ), typeof( Axes ), new FrameworkPropertyMetadata( 100d, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, MinimumTickSpacingChangedStatic ) );
		public double MinimumTickSpacing {
			get { return (double)GetValue( MinimumTickSpacingProperty ); }
			set { SetValue( MinimumTickSpacingProperty, value ); }
		}
		public static double GetMinimumTickSpacing( DependencyObject element ) { return (double)element.GetValue( MinimumTickSpacingProperty ); }
		public static void SetMinimumTickSpacing( DependencyObject element, double value ) { element.SetValue( MinimumTickSpacingProperty, value ); }
		private static void MinimumTickSpacingChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: ScaleTextSize
		public static readonly DependencyProperty ScaleTextSizeProperty = DependencyProperty.RegisterAttached( "ScaleTextSize", typeof( double ), typeof( Axes ), new FrameworkPropertyMetadata( 10d, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, ScaleTextSizeChangedStatic ) );
		public double ScaleTextSize {
			get { return (double)GetValue( ScaleTextSizeProperty ); }
			set { SetValue( ScaleTextSizeProperty, value ); }
		}
		public static double GetScaleTextSize( DependencyObject element ) { return (double)element.GetValue( ScaleTextSizeProperty ); }
		public static void SetScaleTextSize( DependencyObject element, double value ) { element.SetValue( ScaleTextSizeProperty, value ); }
		private static void ScaleTextSizeChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: MajorGridlines
		public static readonly DependencyProperty MajorGridlinesProperty = DependencyProperty.RegisterAttached( "MajorGridlines", typeof( Pen ), typeof( Axes ), new FrameworkPropertyMetadata( new Pen( Brushes.Black, 1 ), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, MajorGridlinesChangedStatic ) );
		public Pen MajorGridlines {
			get { return (Pen)GetValue( MajorGridlinesProperty ); }
			set { SetValue( MajorGridlinesProperty, value ); }
		}
		public static Pen GetMajorGridlines( DependencyObject element ) { return (Pen)element.GetValue( MajorGridlinesProperty ); }
		public static void SetMajorGridlines( DependencyObject element, Pen value ) { element.SetValue( MajorGridlinesProperty, value ); }
		private static void MajorGridlinesChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: MinorGridlines
		public static readonly DependencyProperty MinorGridlinesProperty = DependencyProperty.RegisterAttached( "MinorGridlines", typeof( Pen ), typeof( Axes ), new FrameworkPropertyMetadata( new Pen( Brushes.Gray, 0.5 ), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, MinorGridlinesChangedStatic ) );
		public Pen MinorGridlines {
			get { return (Pen)GetValue( MinorGridlinesProperty ); }
			set { SetValue( MinorGridlinesProperty, value ); }
		}
		public static Pen GetMinorGridlines( DependencyObject element ) { return (Pen)element.GetValue( MinorGridlinesProperty ); }
		public static void SetMinorGridlines( DependencyObject element, Pen value ) { element.SetValue( MinorGridlinesProperty, value ); }
		private static void MinorGridlinesChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: ScaleTextBrush
		public static readonly DependencyProperty ScaleTextBrushProperty = DependencyProperty.RegisterAttached( "ScaleTextBrush", typeof( Brush ), typeof( Axes ), new FrameworkPropertyMetadata( Brushes.Black, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, ScaleTextBrushChangedStatic ) );
		public Brush ScaleTextBrush {
			get { return (Brush)GetValue( ScaleTextBrushProperty ); }
			set { SetValue( ScaleTextBrushProperty, value ); }
		}
		public static Brush GetScaleTextBrush( DependencyObject element ) { return (Brush)element.GetValue( ScaleTextBrushProperty ); }
		public static void SetScaleTextBrush( DependencyObject element, Brush value ) { element.SetValue( ScaleTextBrushProperty, value ); }
		private static void ScaleTextBrushChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: Typeface
		public static readonly DependencyProperty TypefaceProperty = DependencyProperty.RegisterAttached( "Typeface", typeof( Typeface ), typeof( Axes ), new FrameworkPropertyMetadata( new Typeface( "Times New Roman" ), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, TypefaceChangedStatic ) );
		public Typeface Typeface {
			get { return (Typeface)GetValue( TypefaceProperty ); }
			set { SetValue( TypefaceProperty, value ); }
		}
		public static Typeface GetTypeface( DependencyObject element ) { return (Typeface)element.GetValue( TypefaceProperty ); }
		public static void SetTypeface( DependencyObject element, Typeface value ) { element.SetValue( TypefaceProperty, value ); }
		private static void TypefaceChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: ShowLabels
		public static readonly DependencyProperty ShowLabelsProperty = DependencyProperty.RegisterAttached( "ShowLabels", typeof( bool ), typeof( Axes ), new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, ShowLabelsChangedStatic ) );
		public bool ShowLabels {
			get { return (bool)GetValue( ShowLabelsProperty ); }
			set { SetValue( ShowLabelsProperty, value ); }
		}
		public static bool GetShowLabels( DependencyObject element ) { return (bool)element.GetValue( ShowLabelsProperty ); }
		public static void SetShowLabels( DependencyObject element, bool value ) { element.SetValue( ShowLabelsProperty, value ); }
		private static void ShowLabelsChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: AxisLabels
		private static readonly DependencyPropertyKey AxisLabelsPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "AxisLabels", typeof( ObservableCollection<object> ), typeof( Axes ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, AxisLabelsChangedStatic ) );
		public static readonly DependencyProperty AxisLabelsProperty = AxisLabelsPropertyKey.DependencyProperty;
		public ObservableCollection<object> AxisLabels {
			get { return (ObservableCollection<object>)GetValue( AxisLabelsProperty ); }
			private set { SetValue( AxisLabelsPropertyKey, value ); }
		}
		private static void AxisLabelsChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		public IEnumerable<Plot> Plots {
			get { return InternalChildren.OfType<Plot>(); }
		}

		static Axes() {
			GraphingRegistry.RegisterAxes( CartesianAxes.Key, typeof( CartesianAxes ), null );
			GraphingRegistry.RegisterAxes( SmithAxes.Key, typeof( SmithAxes ), null );
			GraphingRegistry.RegisterAxes( PolarAxes.Key, typeof( PolarAxes ), null );
			Graphing.Plots.Plots2D.Load();
		}
		public Axes() {
			AxisLabels = new ObservableCollection<object>();
		}

		public GlyphRun CreateGlyphRun( string text, double textSize, Typeface typeface, Point? origin = null ) {
			Size sz;
			return CreateGlyphRun( text, textSize, typeface, out  sz, origin );
		}
		public GlyphRun CreateGlyphRun( string text, double textSize, Typeface typeface, out Size size, Point? origin = null ) {

			if (text.Length == 0)
				text = " ";

			GlyphTypeface gt;
			if (!typeface.TryGetGlyphTypeface( out gt ))
				throw new ArgumentException( "No matching GlyphTypeface", "typeface" );

			ushort[] glyphIndices = new ushort[text.Length];
			double[] advWidths = new double[text.Length];

			double totalWidth = 0;
			double maxHeight = 0;

			ushort idx;
			for (int i = 0; i < text.Length; i++) {
				idx = glyphIndices[i] = gt.CharacterToGlyphMap[text[i]];

				totalWidth +=
					advWidths[i] = gt.AdvanceWidths[idx] * textSize;
				maxHeight = Math.Max( maxHeight, gt.AdvanceHeights[idx] * textSize );
			}

			if (text.Length == 0)
				size = new Size( 0, 0 );
			else
				size = new Size( totalWidth, maxHeight );


			return new GlyphRun( gt, 0, false, textSize, glyphIndices, origin ?? new Point( 0, 0 ), advWidths, null, null, null, null, null, null );
		}

		protected override Size MeasureOverride( Size availableSize ) {
			return new Size( 0, 0 );
		}

		protected override void OnVisualParentChanged( DependencyObject oldParent ) {
			base.OnVisualParentChanged( oldParent );
			RaiseEvent( new RoutedEventArgs( AxesLoadedEvent, this ) );
		}

		protected virtual void OnAddPlot( Plot plot ) {
			plot.DataChanged += OnPlotDataChanged;
		}
		protected virtual void OnRemovePlot( Plot plot ) {
			plot.DataChanged -= OnPlotDataChanged;
			plot.AxesTransform = null;
		}

		protected virtual void OnPlotDataChanged( object sender, NotifySeriesChangedEventArgs e ) { }

		protected override void OnVisualChildrenChanged( DependencyObject visualAdded, DependencyObject visualRemoved ) {
			base.OnVisualChildrenChanged( visualAdded, visualRemoved );

			if (visualAdded is Plot)
				OnAddPlot( (Plot)visualAdded );

			if (visualRemoved is Plot)
				OnRemovePlot( (Plot)visualRemoved );
		}
		protected override void OnRenderSizeChanged( SizeChangedInfo sizeInfo ) {
			base.OnRenderSizeChanged( sizeInfo );
			InvalidateMeasure();
		}

		protected override void OnRender( DrawingContext cx ) {
			cx.DrawRectangle( Background, null, new Rect( RenderSize ) );
		}
	}

	public abstract class MultiAxes : Axes {
		private static readonly ObservableCollection<AxisSetDefinition> DefaultAxisSetDefinitions = new ObservableCollection<AxisSetDefinition>( new AxisSetDefinition[] { new AxisSetDefinition( "Default" ) { Name = "No Definition", Axes = new List<AxisDefinition>( new AxisDefinition[] { new AxisDefinition( "Default" ) { AxisName = "No Axes Defined" } } ) } } );

		#region DP: AxisSet (Attached Only)
		public static readonly DependencyProperty AxisSetProperty = DependencyProperty.RegisterAttached( "AxisSet", typeof( int ), typeof( MultiAxes ), new FrameworkPropertyMetadata( 0, FrameworkPropertyMetadataOptions.AffectsParentMeasure, AxisSetChangedStatic ) );
		public static int GetAxisSet( Plot element ) { return (int)element.GetValue( AxisSetProperty ); }
		public static void SetAxisSet( Plot element, int value ) { element.SetValue( AxisSetProperty, value ); }
		private static void AxisSetChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
			if (sender is UIElement) {
				((UIElement)sender).RaiseEvent( new RoutedPropertyChangedEventArgs<int>( (int)e.OldValue, (int)e.NewValue, AxisSetChangedEvent ) );
			}
		}

		public static readonly RoutedEvent AxisSetChangedEvent = EventManager.RegisterRoutedEvent( "AxisSetChanged", RoutingStrategy.Bubble, typeof( RoutedPropertyChangedEventHandler<int> ), typeof( MultiAxes ) );
		public static void AddAxisSetChangedHandler( DependencyObject d, RoutedPropertyChangedEventHandler<int> handler ) {
			if (d != null && d is UIElement)
				((UIElement)d).AddHandler( AxisSetChangedEvent, handler );
		}
		public static void RemoveAxisSetChangedHandler( DependencyObject d, RoutedPropertyChangedEventHandler<int> handler ) {
			if (d != null && d is UIElement)
				((UIElement)d).RemoveHandler( AxisSetChangedEvent, handler );
		}

		#endregion

		#region DP: AxisSetDefinitions
		private static readonly DependencyPropertyKey AxisSetDefinitionsPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "AxisSetDefinitions", typeof( ObservableCollection<AxisSetDefinition> ), typeof( MultiAxes ), new FrameworkPropertyMetadata( DefaultAxisSetDefinitions, FrameworkPropertyMetadataOptions.None, AxisSetDefinitionsChangedStatic ) );
		public static readonly DependencyProperty AxisSetDefinitionsProperty = AxisSetDefinitionsPropertyKey.DependencyProperty;
		public ObservableCollection<AxisSetDefinition> AxisSetDefinitions {
			get { return (ObservableCollection<AxisSetDefinition>)GetValue( AxisSetDefinitionsProperty ); }
			protected set { SetValue( AxisSetDefinitionsPropertyKey, value ); }
		}
		public static ObservableCollection<AxisSetDefinition> GetAxisSetDefinitions( DependencyObject element ) { return (ObservableCollection<AxisSetDefinition>)element.GetValue( AxisSetDefinitionsProperty ); }
		private static void SetAxisSetDefinitionsForType( DependencyObject element, ObservableCollection<AxisSetDefinition> value ) { element.SetValue( AxisSetDefinitionsProperty, value ); }
		private static void AxisSetDefinitionsChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
			MultiAxes ax = sender as MultiAxes;
			if (ax != null) {
				ObservableCollection<AxisSetDefinition> oc = e.OldValue as ObservableCollection<AxisSetDefinition>;
				if (oc != null)
					oc.CollectionChanged -= ax.OnAxisSetDefinitionCollectionChanged;
				oc = e.NewValue as ObservableCollection<AxisSetDefinition>;
				if (oc != null)
					oc.CollectionChanged += ax.OnAxisSetDefinitionCollectionChanged;
			}
		}
		#endregion

		#region DP: AxisLabelsEditable
		private static readonly DependencyPropertyKey AxisLabelsEditablePropertyKey = DependencyProperty.RegisterAttachedReadOnly( "AxisLabelsEditable", typeof( bool ), typeof( MultiAxes ), new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.None ) );
		public static readonly DependencyProperty AxisLabelsEditableProperty = AxisLabelsEditablePropertyKey.DependencyProperty;
		public bool AxisLabelsEditable {
			get { return (bool)GetValue( AxisLabelsEditableProperty ); }
			private set { SetValue( AxisLabelsEditableProperty, value ); }
		}
		public static bool GetAxisLabelsEditable( DependencyObject element ) { return (bool)element.GetValue( AxisLabelsEditableProperty ); }
		private static void SetAxisLabelsEditable( DependencyObject element, bool value ) { element.SetValue( AxisLabelsEditableProperty, value ); }
		#endregion

		protected static void SetAxisSetDefinitionsForType( Type type, params AxisSetDefinition[] definitions ) { SetAxisSetDefinitionsForType( type, true, definitions ); }
		protected static void SetAxisSetDefinitionsForType( Type type, bool labelsEditable, params AxisSetDefinition[] definitions ) {
			AxisSetDefinitionsPropertyKey.OverrideMetadata( type, new FrameworkPropertyMetadata(
				new ObservableCollection<AxisSetDefinition>( definitions ),
				FrameworkPropertyMetadataOptions.None,
				AxisSetDefinitionsChangedStatic
			) );
			AxisLabelsEditablePropertyKey.OverrideMetadata( type, new FrameworkPropertyMetadata( labelsEditable ) );
		}

		public MultiAxes( int Dimensions ) {
			this.Dimensions = Dimensions;
			//Listen for changes in axis definitions (such as axis labels being changed)
			foreach (AxisSetDefinition def in AxisSetDefinitions)
				def.PropertyChanged += OnAxisDefinitionChanged;

			//Listen for changes to the axis definitions (only necessary if the object changes its axes definitions from the class default)
			AxisSetDefinitions.CollectionChanged += OnAxisSetDefinitionCollectionChanged;

			//Listen for plots changing axis set
			AddHandler( AxisSetChangedEvent, new RoutedPropertyChangedEventHandler<int>( delegate( object sender, RoutedPropertyChangedEventArgs<int> e ) {
				OnAxisSetChanged( e.OriginalSource as Plot, e.OldValue, e.NewValue );
			} ) );
		}

		protected virtual void OnAxisSetChanged( Plot plot, int oldAxisSet, int newAxisSet ) { }

		protected virtual void OnAxisSetDefinitionCollectionChanged( object sender, NotifyCollectionChangedEventArgs e ) {
			if (e.OldItems != null && e.OldItems.Count > 0)
				foreach (AxisSetDefinition def in e.OldItems)
					def.PropertyChanged -= OnAxisDefinitionChanged;

			if (e.NewItems != null && e.NewItems.Count > 0)
				foreach (AxisSetDefinition def in e.NewItems)
					def.PropertyChanged += OnAxisDefinitionChanged;
		}
		protected virtual void OnAxisDefinitionChanged( object sender, PropertyChangedEventArgs e ) {
		}
	}
	public class AxisSetDefinition : INotifyPropertyChanged, INotifyCollectionChanged {

		private string _name;
		public string Name {
			get { return _name; }
			set { _name = value; RaisePropertyChanged( "Name" ); }
		}

		private readonly ObservableCollection<AxisDefinition> _axisNames = new ObservableCollection<AxisDefinition>();
		public IList<AxisDefinition> Axes {
			get { return _axisNames; }
			set {
				_axisNames.Clear();
				foreach (AxisDefinition ad in value)
					_axisNames.Add( ad );
				RaisePropertyChanged( "Axes" );
			}
		}

		public AxisSetDefinition( string name ) {
			Name = name;
			_axisNames.CollectionChanged += OnAxisNamesChanged;
		}
		public AxisSetDefinition( string name, params AxisDefinition[] axes )
			: this( name ) {
			Axes = axes;
		}

		public AxisDefinition this[int idx] {
			get { return _axisNames[idx]; }
			set { _axisNames[idx] = value; }
		}

		public event PropertyChangedEventHandler PropertyChanged;
		private void RaisePropertyChanged( string propName ) {
			if (PropertyChanged != null)
				PropertyChanged( this, new PropertyChangedEventArgs( propName ) );
		}
		private void AxisPropertyChanged( object sender, PropertyChangedEventArgs e ) {
			if (PropertyChanged != null)
				PropertyChanged( sender, e );
		}

		public event NotifyCollectionChangedEventHandler CollectionChanged;
		private void OnAxisNamesChanged( object sender, NotifyCollectionChangedEventArgs e ) {
			if (e.OldItems != null && e.OldItems.Count > 0)
				foreach (AxisDefinition def in e.OldItems)
					def.PropertyChanged -= AxisPropertyChanged;

			if (e.NewItems != null && e.NewItems.Count > 0)
				foreach (AxisDefinition def in e.NewItems)
					def.PropertyChanged += AxisPropertyChanged;

			if (CollectionChanged != null)
				CollectionChanged( this, e );
		}
	}

	public class AxisDefinition : INotifyPropertyChanged {
		private String _axisName;
		public String AxisName {
			get { return _axisName; }
			set { _axisName = value; RaisePropertyChanged( "AxisName" ); }
		}
		private String _axisLabel;
		public String AxisLabel {
			get { return _axisLabel; }
			set { _axisLabel = value; RaisePropertyChanged( "AxisLabel" ); }
		}

		public AxisDefinition() { }
		public AxisDefinition( string name, string label )
			: this() {
			AxisName = name;
			AxisLabel = label;
		}
		public AxisDefinition( string nameLabel ) : this( nameLabel, nameLabel ) { }

		public event PropertyChangedEventHandler PropertyChanged;
		private void RaisePropertyChanged( string propName ) {
			if (PropertyChanged != null)
				PropertyChanged( this, new PropertyChangedEventArgs( propName ) );
		}
	}
}
